package com.ctgu.demo.controller;

import com.ctgu.demo.entity.UserEntity;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.domain.geo.GeoLocation;
import org.springframework.data.redis.domain.geo.GeoReference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/users")
public class UserController {

    private final RedisTemplate<String, Object> redisTemplate;

    public UserController(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    private static final String TEST1_REDIS_KEY = "test1:";
    private static final String TEST2_REDIS_KEY = "test2:";
    private static final String TEST3_REDIS_KEY = "test3:";
    private static final String TEST4_REDIS_KEY = "test4:";
    private static final String TEST5_REDIS_KEY = "test5:";
    private static final String TEST6_REDIS_KEY = "test6:";

    private static final String START_COUNT = "start_count";

    //http://localhost:8080/users/test1
    @GetMapping("/test1")
    public void test1() {
        UserEntity userEntity1 = new UserEntity(1, "Alex", "123456");
        UserEntity userEntity2 = new UserEntity(2, "Alice", "233333");

        // 1.Boolean expire(K key, long timeout, TimeUnit unit)
        redisTemplate.opsForValue().set(TEST1_REDIS_KEY + userEntity1.getId(), userEntity1);
        redisTemplate.expire(TEST1_REDIS_KEY + userEntity1.getId(), 1, TimeUnit.MINUTES);

        // 2.Boolean hasKey(K key)
        Boolean result1 = redisTemplate.hasKey(TEST1_REDIS_KEY + userEntity1.getId());
        System.out.println(result1);        //true

        // 3.Boolean delete(K key)
        Boolean result2 = redisTemplate.delete(TEST1_REDIS_KEY + userEntity1.getId());
        System.out.println(result2);        //true
    }

    //http://localhost:8080/users/test2
    @GetMapping("/test2")
    public void test2() {
        UserEntity userEntity1 = new UserEntity(1, "Alex", "123456");
        UserEntity userEntity2 = new UserEntity(2, "Alice", "233333");
        UserEntity userEntity3 = new UserEntity(3, "Tom", "666666");
        UserEntity userEntity4 = new UserEntity(4, "Jerry", "888888");

        // 1.void set(K key, V value)
        redisTemplate.opsForValue().set(TEST2_REDIS_KEY + userEntity1.getId(), userEntity1);
        redisTemplate.expire(TEST2_REDIS_KEY + userEntity1.getId(), 1, TimeUnit.MINUTES);

        // 2.void set(K key, V value, long timeout, TimeUnit unit)
        redisTemplate.opsForValue().set(TEST2_REDIS_KEY + userEntity2.getId(), userEntity2, 1, TimeUnit.MINUTES);

        // 3.V get(Object key)
        Object object = redisTemplate.opsForValue().get(TEST2_REDIS_KEY + userEntity2.getId());
        ObjectMapper objectMapper = new ObjectMapper();
        UserEntity userEntity = objectMapper.convertValue(object, UserEntity.class);
        System.out.println(userEntity);     //UserEntity(id=2, username=Alice, password=233333)

        // 4.Long increment(K key)
        Long result5 = redisTemplate.opsForValue().increment(START_COUNT);
        redisTemplate.expire(START_COUNT, 1, TimeUnit.MINUTES);
        System.out.println(result5);        //1

        // 5.Long increment(K key, long delta)
        Long result6 = redisTemplate.opsForValue().increment(START_COUNT, 5);
        System.out.println(result6);        //6
    }

    //http://localhost:8080/users/test3
    @GetMapping("/test3")
    public void test3() {
        UserEntity userEntity1 = new UserEntity(1, "Alex", "123456");
        UserEntity userEntity2 = new UserEntity(2, "Alice", "233333");
        UserEntity userEntity3 = new UserEntity(3, "Tom", "666666");

        // 1.void put(H key, HK hashKey, HV value)
        redisTemplate.opsForHash().put(TEST4_REDIS_KEY + userEntity1.getId(), "username", userEntity1.getUsername());
        redisTemplate.opsForHash().put(TEST4_REDIS_KEY + userEntity1.getId(), "password", userEntity1.getPassword());
        redisTemplate.expire(TEST4_REDIS_KEY + userEntity1.getId(), 1, TimeUnit.MINUTES);

        // 2.void putAll(H key, Map<? extends HK, ? extends HV> m)
        HashMap<String, String> map = new HashMap<>();
        map.put("username", userEntity2.getUsername());
        map.put("password", userEntity2.getPassword());
        redisTemplate.opsForHash().putAll(TEST4_REDIS_KEY + userEntity2.getId(), map);
        redisTemplate.expire(TEST4_REDIS_KEY + userEntity2.getId(), 1, TimeUnit.MINUTES);

        // 3.HV get(H key, Object hashKey)
        String username = (String) redisTemplate.opsForHash().get(TEST4_REDIS_KEY + userEntity1.getId(), "username");
        String password = (String) redisTemplate.opsForHash().get(TEST4_REDIS_KEY + userEntity1.getId(), "password");
        System.out.println(username);       //Alex
        System.out.println(password);       //123456

        // 4.Long delete(H key, Object... hashKeys)
        Boolean result4 = redisTemplate.delete(TEST4_REDIS_KEY + userEntity1.getId());
        System.out.println(result4);        //true

        // 5.Map<HK, HV> entries(H key)
        Map<Object, Object> entries = redisTemplate.opsForHash().entries(TEST4_REDIS_KEY + userEntity1.getId());
        entries.forEach((k, v) -> System.out.println(k + ":" + v));
//        username:Alex
//        password:123456

        // 6.Set<HK> keys(H key)
        Set<Object> keys = redisTemplate.opsForHash().keys(TEST4_REDIS_KEY + userEntity1.getId());
        keys.forEach(key -> System.out.println("key:" + key));
//        key:username
//        key:password

        // 7.List<HV> values(H key)
        List<Object> values = redisTemplate.opsForHash().values(TEST4_REDIS_KEY + userEntity1.getId());
        values.forEach(value -> System.out.println("value:" + value));
//        value:Alex
//        value:123456
    }

    //http://localhost:8080/users/test4
    @GetMapping("/test4")
    public void test4() {
        UserEntity userEntity1 = new UserEntity(1, "Alex", "123456");
        UserEntity userEntity2 = new UserEntity(2, "Alice", "233333");
        UserEntity userEntity3 = new UserEntity(3, "Tom", "666666");
        UserEntity userEntity4 = new UserEntity(4, "Jerry", "888888");
        List<UserEntity> list = Arrays.asList(userEntity1, userEntity2, userEntity3, userEntity4);

        // 1.FILO 先进后出 栈 : Long leftPush(K key, V value) + V leftPop(K key)
        list.forEach(userEntity -> redisTemplate.opsForList().leftPush(TEST3_REDIS_KEY, userEntity));
        while (true) {
            Object object = redisTemplate.opsForList().leftPop(TEST3_REDIS_KEY);
            ObjectMapper objectMapper = new ObjectMapper();
            UserEntity userEntity = objectMapper.convertValue(object, UserEntity.class);
            if (userEntity == null) {
                break;
            }
            System.out.println(userEntity);
        }
//        UserEntity(id=4, username=Jerry, password=888888)
//        UserEntity(id=3, username=Tom, password=666666)
//        UserEntity(id=2, username=Alice, password=233333)
//        UserEntity(id=1, username=Alex, password=123456)


        // 2.FIFO 先进先出 队列 : Long leftPush(K key, V value) + V rightPop(K key)
        list.forEach(userEntity -> redisTemplate.opsForList().leftPush(TEST3_REDIS_KEY, userEntity));
        while (true) {
            Object object = redisTemplate.opsForList().rightPop(TEST3_REDIS_KEY);
            ObjectMapper objectMapper = new ObjectMapper();
            UserEntity userEntity = objectMapper.convertValue(object, UserEntity.class);
            if (userEntity == null) {
                break;
            }
            System.out.println(userEntity);
        }
//        UserEntity(id=1, username=Alex, password=123456)
//        UserEntity(id=2, username=Alice, password=233333)
//        UserEntity(id=3, username=Tom, password=666666)
//        UserEntity(id=4, username=Jerry, password=888888)

        // 3.List<V> range(K key, long start, long end)
        list.forEach(userEntity -> redisTemplate.opsForList().leftPush(TEST3_REDIS_KEY, userEntity));
        List<Object> objectList = redisTemplate.opsForList().range(TEST3_REDIS_KEY, 0, -1);
        ObjectMapper objectMapper = new ObjectMapper();
        if (objectList != null) {
            objectList.forEach(object -> {
                UserEntity userEntity = objectMapper.convertValue(object, UserEntity.class);
                System.out.println(userEntity);
            });
        }
//        UserEntity(id=4, username=Jerry, password=888888)
//        UserEntity(id=3, username=Tom, password=666666)
//        UserEntity(id=2, username=Alice, password=233333)
//        UserEntity(id=1, username=Alex, password=123456)
    }

    //http://localhost:8080/users/test5
    @GetMapping("/test5")
    public void test5() {
        // 1.Long add(K key, V... values)
        Long result1 = redisTemplate.opsForSet().add(TEST5_REDIS_KEY, "Alex", "Alice", "Tom", "Jerry");
        System.out.println(result1);        //1

        // 2.Long remove(K key, Object... values)
        Long result5 = redisTemplate.opsForSet().remove(TEST5_REDIS_KEY);
        System.out.println(result5);

        // 3.Boolean isMember(K key, Object o)
        Boolean result2 = redisTemplate.opsForSet().isMember(TEST5_REDIS_KEY, "Tom");
        System.out.println(result2);        //true

        // 4.Set<V> members(K key)
        Set<Object> members = redisTemplate.opsForSet().members(TEST5_REDIS_KEY);
        if (members != null) {
            members.forEach(set -> System.out.println("set:" + set));
        }
//        set:Alice
//        set:Jerry
//        set:Tom
//        set:Alex

        redisTemplate.opsForSet().add("group:A", "Alice", "Bob", "Charlie");
        redisTemplate.opsForSet().add("group:B", "Bob", "Charlie", "David");

        // 5.intersect(K key, Collection<K> otherKeys)
        Set<Object> intersection = redisTemplate.opsForSet().intersect("group:A", Collections.singletonList("group:B"));
        System.out.println(intersection);   //[Bob, Charlie]

        redisTemplate.opsForSet().intersect("group A", Collections.singletonList("group:B"));
        Set<String> groupC = Collections.singleton("Group C");
        groupC.forEach(System.out::println);

        // 6.union(K key, Collection<K> otherKeys)
        Set<Object> union = redisTemplate.opsForSet().union("group:A", Collections.singletonList("group:B"));
        System.out.println(union);          //[Bob, Alice, Charlie, David]

        // 7.difference(K key, Collection<K> otherKeys)
        Set<Object> difference = redisTemplate.opsForSet().difference("group:A", Collections.singletonList("group:B"));
        System.out.println(difference);     //[Alice]
    }

    //http://localhost:8080/users/test6
    @GetMapping("/test6")
    public void test6() {
        // 1.Boolean add(K key, V value, double score)
        Boolean result1 = redisTemplate.opsForZSet().add(TEST6_REDIS_KEY, "Alex", 100.0);
        Boolean result2 = redisTemplate.opsForZSet().add(TEST6_REDIS_KEY, "Alice", 90.0);
        Boolean result3 = redisTemplate.opsForZSet().add(TEST6_REDIS_KEY, "Tom", 80.0);
        Boolean result4 = redisTemplate.opsForZSet().add(TEST6_REDIS_KEY, "Jerry", 70.0);
        System.out.println(result1);    //true
        System.out.println(result2);    //true
        System.out.println(result3);    //true
        System.out.println(result4);    //true

        // 2.range(K key, long start, long end)
        Set<Object> sets1 = redisTemplate.opsForZSet().range(TEST6_REDIS_KEY, 0, -1);
        if (sets1 != null) {
            sets1.forEach(set -> System.out.println("set:" + set));
        }
//        set:Jerry
//        set:Tom
//        set:Alice
//        set:Alex

        // 3.Set<V> rangeByScore(K key, double min, double max)
        Set<Object> sets2 = redisTemplate.opsForZSet().rangeByScore(TEST6_REDIS_KEY, 0, 80);
        if (sets2 != null) {
            sets2.forEach(set -> System.out.println("set:" + set));
        }
//        set:Jerry
//        set:Tom

        // 4.Long rank(K key, Object value)
        Long result5 = redisTemplate.opsForZSet().rank(TEST6_REDIS_KEY, "Tom");
        System.out.println(result5);    //1

        // 5.Double score(K key, Object o)
        Double result6 = redisTemplate.opsForZSet().score(TEST6_REDIS_KEY, "Tom");
        System.out.println(result6);    //80.0

        // 6.Long remove(K key, Object... values)
        Long result7 = redisTemplate.opsForZSet().remove(TEST6_REDIS_KEY, "Alex", "Alice");
        System.out.println(result7);    //2

        // 7.Double incrementScore(K key, V value, double delta)
        Double result8 = redisTemplate.opsForZSet().incrementScore(TEST6_REDIS_KEY, "Tom", -5.0);
        System.out.println(result8);    //75.0
    }

    //http://localhost:8080/users/test7
    @GetMapping("/test7")
    public void test7() {
        // 1.Long add(K key, Point point, M member)
        redisTemplate.opsForGeo().add("locations", new Point(13.361389, 38.115556), "Palermo");
        redisTemplate.opsForGeo().add("locations", new Point(15.087269, 37.502669), "Catania");

        // 2.List<Point> position(K key, M... members)
        List<Point> locations = redisTemplate.opsForGeo().position("locations", "Palermo", "Catania");
        if (locations != null && !locations.isEmpty()) {
            for (Point location : locations) {
                System.out.println("Location: " + location);
            }
        }

        // 3.Distance distance(K key, M member1, M member2, Metric metric)
        Distance distance = redisTemplate.opsForGeo().distance("locations", "Palermo", "Catania", RedisGeoCommands.DistanceUnit.METERS);
        if (distance != null) {
            System.out.println("Distance: " + distance.getValue() + " " + distance.getUnit());
        }

        // 4.GeoResults<RedisGeoCommands.GeoLocation<M>> search(K key, GeoReference<M> reference, Distance radius, RedisGeoCommands.GeoSearchCommandArgs args)
        GeoResults<RedisGeoCommands.GeoLocation<Object>> results = redisTemplate.opsForGeo().search(
                "locations",
                GeoReference.fromCoordinate(15.087269, 37.502669),      // 查询以给定的经纬度为中心的圆形区域
                new Distance(1000000),                                            // 查询100km范围内的店铺，单位默认为米
                RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().includeDistance());
        if (results != null) {
            results.forEach(result -> {
                System.out.println("Name : " + result.getContent().getName() + ", Distance : " + result.getDistance());
            });
        }
    }

    //http://localhost:8080/users/test8
    @GetMapping("/test8")
    public void test8() {
        // 1.Boolean setBit(K key, long offset, boolean value)
        redisTemplate.opsForValue().setBit("bitmap", 3, true);

        // 2.Boolean getBit(K key, long offset)
        boolean bitValue = Boolean.TRUE.equals(redisTemplate.opsForValue().getBit("bitmap", 3));
        System.out.println(bitValue);

        // 3.List<Long> bitField(K key, BitFieldSubCommands subCommands)
        BitFieldSubCommands subCommands = BitFieldSubCommands.create()
                .get(BitFieldSubCommands.BitFieldType.INT_8).valueAt(0);        // 获取位图中的第一个8位00010000=16
        List<Long> result = redisTemplate.opsForValue().bitField("bitmap", subCommands);
        System.out.println(result);
    }

    //http://localhost:8080/users/test9
    @GetMapping("/test9")
    public void test9() {
        Map<String, Object> result = new HashMap<>();

        // 1.Long add(K key, V... values)
        redisTemplate.opsForHyperLogLog().add("hll", "user1", "user2", "user3");
        result.put("addedElements", Arrays.asList("user1", "user2", "user3"));
        System.out.println(result);

        // 2.Long size(K... keys)
        Long count = redisTemplate.opsForHyperLogLog().size("hll");
        result.put("hllCount", count);
        System.out.println(result);

        // 3.Long union(K destination, K... sourceKeys)
        redisTemplate.opsForHyperLogLog().add("hll1", "user4", "user5");
        redisTemplate.opsForHyperLogLog().add("hll2", "user6", "user7");
        redisTemplate.opsForHyperLogLog().union("hll_union", "hll1", "hll2");
        Long unionCount = redisTemplate.opsForHyperLogLog().size("hll_union");
        result.put("hllUnionCount", unionCount);
        System.out.println(result);

        // 4.void delete(K key)
        redisTemplate.delete("hll");
        redisTemplate.delete("hll1");
        redisTemplate.delete("hll2");
        redisTemplate.delete("hll_union");
        result.put("deleted", true);
        System.out.println(result);
    }
}